/**
 * @file Date.cpp
 * 
 * This file contains the definitions of all the funcitons to manipulate the data
 * given by the ctime library and its corresponding routines.
 */
#include "DateTime.hpp"
#include <iostream>
using namespace util;

/**
 * Default constructor
 */
DateTime::DateTime()
{
	time = 0;
	time_struct = *localtime( &time );
}

/**
 * Constructor with the value already declared
 * @param name: time - description: a time_t type variable (int)
 */
DateTime::DateTime( time_t time )
{
	this->time = time;
	tm* temp_pointer;

	if( (temp_pointer = localtime(&time)) == NULL)
	{
		this->time = 0;
		time_struct = *localtime( &this->time );
		throw InvalidDateTimeValue( "Invalid constructor" );
	}
	time_struct = *temp_pointer;
}

/**
 * Constructor that parallels the set methods
 * @param names: year, month, day, hour, minute, second.
 */
DateTime::DateTime( int year, int month, int day, int hour, int minute, int second )
{
	time_struct.tm_year = year - 1900; 
    time_struct.tm_mon = month - 1;
    time_struct.tm_mday = day;
    time_struct.tm_hour = hour;
    time_struct.tm_min = minute;
    time_struct.tm_sec = second;

	time = mktime( &time_struct );
	if( time < 0 )
	{
		time = 0;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Individual constructor error" );
	}
}

/**
 * Returns an object representing the current time (as returned by the time function).
 */
DateTime util::DateTime::now()
{
	time_t now = ::time ( NULL );
	DateTime Now( now );

	return Now;
}

/**
 * Takes the current time as a parameter and stores it into a struct.
 * The current time is sent as the amount of seconds since Jan 1, 1900. 
 * @param name: time - description: a time_t type variable (int)
 */
void DateTime::set( std::time_t time )
{
	time_t temp_time = this->time;
	tm* temp_pointer;

	if( (temp_pointer = localtime(&time)) == NULL)
	{
		this->time = temp_time;
		throw InvalidDateTimeValue( "Invalid setter" );
	}
	time_struct = *temp_pointer;
}

/**
 * Takes a given date and stores it into a struct.
 * @param names: year, month, day, hour, minute, second.
 */
void DateTime::set( int year, int month, int day, int hour, int minute, int second)
{
    time_struct.tm_year = year - 1900; 
    time_struct.tm_mon = month - 1;
    time_struct.tm_mday = day;
    time_struct.tm_hour = hour;
    time_struct.tm_min = minute;
    time_struct.tm_sec = second;

	time_t temp_time = time;
    time = mktime( &time_struct );
	
	if( time < 0 )
	{
		time = temp_time;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Individual setter error" );
	}
}

/**
 * Allows the user to update the date.
 * @param names: amount, unit - description: amount is an integer to update the unit. 
 * unit corresponds to YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS.
 */
void DateTime::offset( int amount, TimeUnit unit )
{
    if(unit == SECONDS)
    {
        time_struct.tm_sec = time_struct.tm_sec + amount;
		time_t temp_time = time;
        time = mktime ( &time_struct );
		if( time < 0 )
		{
			time = temp_time;
			time_struct = *localtime( &time );
			throw InvalidDateTimeValue( "Invalid seconds" );
		}
    }
    else if(unit == MINUTES)
    {
        time_struct.tm_min = time_struct.tm_min + amount;
        time_t temp_time = time;
        time = mktime ( &time_struct );
		if( time < 0 )
		{
			time = temp_time;
			time_struct = *localtime( &time );
			throw InvalidDateTimeValue( "Invalid minutes" );
		}
    }
    else if(unit == HOURS)
    {
        time_struct.tm_hour = time_struct.tm_hour + amount;
        time_t temp_time = time;
        time = mktime ( &time_struct );
		if( time < 0 )
		{
			time = temp_time;
			time_struct = *localtime( &time );
			throw InvalidDateTimeValue( "Invalid hours" );
		}
    }
    else if(unit == DAYS)
    {
        time_struct.tm_mday = time_struct.tm_mday + amount;
        time_t temp_time = time;
        time = mktime ( &time_struct );
		if( time < 0 )
		{
			time = temp_time;
			time_struct = *localtime( &time );
			throw InvalidDateTimeValue( "Invalid days" );
		}
    }
    else if(unit == MONTHS)
    {
        time_struct.tm_mon = time_struct.tm_mon + amount;
        time_t temp_time = time;
        time = mktime ( &time_struct );
		if( time < 0 )
		{
			time = temp_time;
			time_struct = *localtime( &time );
			throw InvalidDateTimeValue( "Invalid months" );
		}
    }
    else if(unit == YEARS)
    {
        time_struct.tm_year = time_struct.tm_year + amount;
        time_t temp_time = time;
        time = mktime ( &time_struct );
		if( time < 0 )
		{
			time = temp_time;
			time_struct = *localtime( &time );
			throw InvalidDateTimeValue( "Invalid years" );
		}
    }
}

/**
 * Compares two dates to see if they are equal to each other.
 * @param name: other - description: another DateTime object passed by the user.
 */
bool DateTime::equals( const DateTime &other ) const
{
    if(time != other.time)
        return false;
    else
        return true;
}

/**
 * Compares two dates to see if one is before the other one.
 * @param name: other - description: another DateTime object passed by the user.
 */
bool DateTime::before( const DateTime &other ) const
{
    if(time < other.time)
        return true;
    else
        return false;
}

/**
 * Compares two dates to see if one is after the other one.
 * @param name: other - description: another DateTime object passed by the user.
 */
bool DateTime::after( const DateTime &other ) const
{
    if(time > other.time)
        return true;
    else
        return false;
}

/**
 * Returns the date as a string
 */
std::string util::DateTime::getString() const
{
    const int MAX = 80;

	char time_string[MAX];
	strftime( time_string, MAX, format_string.c_str(), &time_struct );

    return time_string;
}

/**
 *Returns new format string to be used for printing the time string.
 */
std::string util::DateTime::getFormatString() 
{
    return DateTime::format_string;
}

/*
 * Takes the format string given by the user and updates the default string.
 * @param name: value - description: a string varaible.
 */
void DateTime::setFormatString( std::string value )
{
	format_string = value;
}
/**
 * Returns the seconds from the struct
 */
int DateTime::getSecond() const
{
    return time_struct.tm_sec;
}

/**
 * Stores the seconds into the struct.
 * @param name: value - description: parameter passed by the user.
 */
void DateTime::setSecond( int value )
{
    time_struct.tm_sec = value;
	time_t temp_time = time;
	time = mktime ( &time_struct );
	if( time < 0 )
	{
		time = temp_time;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Invalid value for second field" );
	}
}

/**
 * Returns the minutes from the struct
 */
int DateTime::getMinute() const
{
    return time_struct.tm_min;
}

/**
 * Stores the minutes into the struct.
 * @param name: value - description: parameter passed by the user.
 */
void DateTime::setMinute( int value )
{
    time_struct.tm_min = value;
	time_t temp_time = time;
    time = mktime( &time_struct );
	
	if( value < 0 )
	{
		time = temp_time;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Invalid value for minute field" );
	}
}

/**
 * Returns the hour from the struct
 */
int DateTime::getHour() const
{
    return time_struct.tm_hour;
}

/**
 * Stores the hour into the struct.
 * @param name: value - description: parameter passed by the user.
 */
void DateTime::setHour( int value )
{
    time_struct.tm_hour= value;
	time_t temp_time = time;
    time = mktime( &time_struct );
	
	if( value < 0 )
	{
		time = temp_time;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Invalid value for hour field" );
	}
}

/**
 * Returns the day from the struct
 */
int DateTime::getDay() const
{
    return time_struct.tm_mday;
}

/**
 * Stores the day into the struct.
 * @param name: value - description: parameter passed by the user.
 */
void DateTime::setDay( int value )
{
    time_struct.tm_mday = value;
	time_t temp_time = time;
	time = mktime ( &time_struct );

	if( value < 0 )
	{
		time = temp_time;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Invalid value for day field" );
	}
}

/**
 * Returns the month from the struct
 */
int DateTime::getMonth() const
{
    return time_struct.tm_mon + 1;
}

/**
 * Stores the month into the struct.
 * @param name: value - description: parameter passed by the user.
 */
void DateTime::setMonth( int value )
{
    time_struct.tm_mon = value - 1;
	time_t temp_time = time;
	time = mktime ( &time_struct );

	if( value < 0 )
	{
		time = temp_time;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Invalid value for month field" );
	}
}

/**
 * Returns the year from the struct
 */
int DateTime::getYear() const
{
    return time_struct.tm_year + 1900;
}

/**
 * Stores the year into the struct.
 * @param name: value - description: parameter passed by the user.
 */
void DateTime::setYear( int value )
{
    time_struct.tm_year = value - 1900;
	time_t temp_time = time;
	time = mktime ( &time_struct );

	if( time < 0 )
	{
		time = temp_time;
		time_struct = *localtime( &time );
		throw InvalidDateTimeValue( "Invalid value for year field" );
	}
}

/**
 * Returns the day of the week from the struct
 */
int DateTime::getDayOfWeek() const
{
    return time_struct.tm_wday;
}

/**
 * Returns the day of the year from the struct
 */
int DateTime::getDayOfYear() const
{
    return time_struct.tm_yday;
}

/*
 *Private variable that holds the default format string.
 */
std::string util::DateTime::format_string = "%a %b %d, %H:%M:%S %Y";

/**
 *Default constructor for invalid date input.
 *@param name: message - description: a string that contains the appropiate message when an exception is being thrown.
 */
InvalidDateTimeValue::InvalidDateTimeValue( std::string message )
{
	error_message = message;
}

/**
 *Returns the specific error message generated by the invalid date input.
 */
const char* InvalidDateTimeValue::what() const
{
	return error_message.c_str();
}